﻿package com.zouloux.core.data.remote 
{
	import adobe.utils.CustomActions;
	import com.greensock.plugins.RemoveTintPlugin;
	import com.zouloux.core.data.IDataItem;
	import com.zouloux.core.debug.Debug;
	import com.zouloux.core.events.RemoteEvent;
	import flash.events.AsyncErrorEvent;
	import flash.events.IOErrorEvent;
	import flash.events.NetStatusEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.NetConnection;
	import flash.net.Responder;
	import nl.demonsters.debugger.MonsterDebugger;
	
	/**
	 * ...
	 * @author ZoulouX
	 */
	public class AMFRemote extends ARemote implements IRemote
	{
		/**
		 * La connexion
		 */
		protected var _netConnection		:NetConnection;
		
		
		/**
		 * Le remote AMF permet de connecter un model à un serveur AMF
		 * @param	pUrl : L'url de service AMF
		 */
		public function AMFRemote (pUrl:String)
		{
			// Créer la connexion
			_netConnection = new NetConnection();
			_netConnection.connect(pUrl);
			
			// Ecouter ce qu'il se passe
			_netConnection.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
			_netConnection.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
			_netConnection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
			_netConnection.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler);
		}
		
		/**
		 * Appeler un service
		 * @param	pServiceName : le nom du service
		 * @param	...rest : les autres paramètres
		 */
		public function call (pServiceName:String, pOptions:Object = null, ... rest):void
		{
			// Créer le responder
			var responder:Responder = new Responder(function (pResponse:*):void
				{
					// Appeler la méthode success interne
					successHandler(pResponse, pOptions);
				},
				function (pResponse:*):void
				{
					// Dispatcher l'erreur interne
					errorHandler(pResponse, pOptions);
				}
			);
			
			// Les arguments
			var args:Array = [pServiceName, responder];
			
			// Y ajouter les paramètres
			args = args.concat(rest);
			
			// Debug
			Debug.core(this, "call", args);
			
			// Appeller le service
			_netConnection.call.apply(_netConnection, args);
			
			// Dispatcher
			dispatchEvent(new RemoteEvent(RemoteEvent.CALL));
		}
		
		/**
		 * Réussite du service, un objet est reçu
		 * @param	pResponse : L'objet de réponse de la part d'AMF
		 */
		protected function successHandler (pResponse:*, pOptions:Object = null):void
		{
			// L'objet de réponse
			var remoteResponse:RemotingResponse;
			
			// Tenter
			try
			{
				// De caster la réponse en RemotingResponse
				remoteResponse = new RemotingResponse(pResponse);	
			}
			catch (error:Error)
			{
				// Créer une RemotingResponse d'erreur
				remoteResponse = new RemotingResponse();
				remoteResponse.success = false;
				remoteResponse.message = "Invalid RemotingResponse received";
				remoteResponse.code = 15;
			}
			
			// L'event de sortie
			var eventOut:RemoteEvent;
			
			// Vérifier la réponse du serveur
			if (remoteResponse.success)
			{
				// Success
				eventOut = new RemoteEvent(RemoteEvent.RESPONSE);
				eventOut.serviceName = remoteResponse.serviceName;
				
				// Les données
				var data:Object = { };
				
				// Enregistrer les dernières données
				_lastData = remoteResponse.response;
				
				// Vérifier si l'on doit parser
				if (_autoParse && parser.items != null)
				{
					// Essayer
					try
					{
						// De parser
						data = parser.parse(remoteResponse.response);
					}
					catch (e:Error)
					{
						// On dispatch un event d'erreur
						parseErrorHandler(remoteResponse.response, pOptions);
					}
				}
				else
					data = remoteResponse.response;
				
				// Enregistrer les dernières données parsée
				_lastResults = data;
				
				// Intégrer les données à l'event
				eventOut.data = data;
				
				// Dispatcher l'event
				dispatchEvent(eventOut);
				
				// Vérifier s'il y a un handler success
				if (pOptions != null && pOptions["onSuccess"])
					pOptions["onSuccess"](data);
			}
			else
			{
				// Une erreur s'est produite, la dispatcher
				eventOut = new RemoteEvent(RemoteEvent.ERROR);
				eventOut.message = remoteResponse.message;
				eventOut.serviceName = remoteResponse.serviceName;
				eventOut.cause = remoteResponse.code;
				eventOut.data = pResponse;
				dispatchEvent(eventOut);
			}
		}
		
		/**
		 * Changement de status par rapport à la connexion
		 * @param	event
		 */
		protected function netStatusHandler (event:NetStatusEvent):void
		{
			Debug.core(this, "netStatusHandler", [event.info.code]);
			
			// L'event d'erreur
			var eventOut:RemoteEvent = new RemoteEvent(RemoteEvent.ERROR);
			eventOut.message = "netStatusError " + event.info.code;
			eventOut.cause = 10;
			dispatchEvent(eventOut);
		}
		
		/**
		 * Erreur asynchrone diverse
		 * @param	event
		 */
		protected function asyncErrorHandler (event:AsyncErrorEvent):void 
		{
			Debug.core(this, "asyncErrorHandler");
			
			// L'event d'erreur
			var eventOut:RemoteEvent = new RemoteEvent(RemoteEvent.ERROR);
			eventOut.message = "asyncError";
			eventOut.cause = 10;
			dispatchEvent(eventOut);
		}
		
		/**
		 * Erreur de sécuritée
		 * @param	event
		 */
		protected function securityErrorHandler (event:SecurityErrorEvent):void 
		{
			Debug.core(this, "securityErrorHandler");
			
			// L'event d'erreur
			var eventOut:RemoteEvent = new RemoteEvent(RemoteEvent.ERROR);
			eventOut.message = "securityError";
			eventOut.cause = 11;
			dispatchEvent(eventOut);
		}
		
		/**
		 * Erreur de transfert
		 * @param	event
		 */
		protected function ioErrorHandler (event:IOErrorEvent):void 
		{
			Debug.core(this, "ioErrorHandler");
			
			// L'event d'erreur
			var eventOut:RemoteEvent = new RemoteEvent(RemoteEvent.ERROR);
			eventOut.message = "ioError";
			eventOut.cause = 12;
			dispatchEvent(eventOut);
		}
		
		/**
		 * Récupérer les erreurs de service
		 * @param	pError : Un objet erreur renvoyé par AMF
		 */
		protected function errorHandler (pError:* = null, pOptions:Object = null):void
		{
			Debug.core(this, "errorHandler");
			MonsterDebugger.trace(this, pError);
			
			// L'event d'erreur
			var eventOut:RemoteEvent = new RemoteEvent(RemoteEvent.ERROR);
			eventOut.message = "callError";
			eventOut.cause = 13;
			eventOut.data = pError;
			dispatchEvent(eventOut);
			
			// Vérifier s'il y a un handler onError
			if (pOptions != null && pOptions["onError"])
				pOptions["onError"](pError);
		}
		
		/**
		 * Erreur de parse
		 */
		protected function parseErrorHandler (pError:* = null, pOptions:Object = null):void
		{
			Debug.core(this, "errorHandler");
			MonsterDebugger.trace(this, pError);
			
			// L'event d'erreur
			var eventOut:RemoteEvent = new RemoteEvent(RemoteEvent.ERROR);
			eventOut.message = "parseErrorHandler";
			eventOut.cause = 14;
			eventOut.data = pError;
			dispatchEvent(eventOut);
			
			// Vérifier s'il y a un handler onError
			if (pOptions != null && pOptions["onError"])
				pOptions["onError"](pError);
		}
	}
}